//
// Created by Panfeng Guo on 2022/11/13.
//
#include <iostream>
#include <sstream>

#include "common/exception.h"
#include "storage/page/b_plus_tree_internal_page.h"

template <typename KeyType, typename ValueType, typename KeyComparator>
void BPlusTreeInternalPage<KeyType, ValueType, KeyComparator>::
Init(page_id_t page_id, page_id_t parent_id)
{
    // step 1: cal max size
    int max_size = (PAGE_SIZE - sizeof(BPlusTreeInternalPage)) / (sizeof(KeyType) + sizeof(ValueType));
    // step 2: set value
    SetPageType(IndexPageType::INTERNAL_PAGE);
    SetSize(1); // set current size: 1 for the first invalid key
    SetPageId(page_id);
    SetParentPageId(parent_id);
    SetMaxSize(max_size);
}

/*!
 * Populate new root page with old_value + new_key & new_value
 * When the insertion cause overflow from leaf page all the way upto the root
 * page, you should create a new root page and populate its elements.
 * NOTE: This method is only called within InsertIntoParent()(b_plus_tree.cpp)
 * @tparam KeyType
 * @tparam ValueType
 * @tparam KeyComparator
 * @param old_value
 * @param new_key
 * @param new_value
 */
template <typename KeyType, typename ValueType, typename KeyComparator>
void BPlusTreeInternalPage<KeyType, ValueType, KeyComparator>::
PopulateNewRoot(const ValueType &old_value, const KeyType &new_key,
                const ValueType &new_value)
{
    // must be an empty page
    assert(GetSize() == 1);
    array[0].second = old_value;
    array[1] = {new_key, new_value};
    IncreaseSize(1);
}

/*!
 * InsertNodeAfter: Insert new_key & new_value pair right after the pair with its value == old_value
 * When the child node is split, the parent node will use this method
 * @tparam KeyType
 * @tparam ValueType
 * @tparam KeyComparator
 * @param old_value
 * @param new_key
 * @param new_value
 * @return: the size after add a new key value
 */
template <typename KeyType, typename ValueType, typename KeyComparator>
int BPlusTreeInternalPage<KeyType, ValueType, KeyComparator>::
InsertNodeAfter(const ValueType &old_value, const KeyType &new_key,
                const ValueType &new_value)
{
    // look from back to front
    for (int i = GetSize(); i > 0; --i){
        if (array[i - 1].second == old_value){
            // 在old_value节点后面插入一个新节点
            array[i] = {new_key, new_value};
            IncreaseSize(1);
            break;
        }
        array[i] = array[i - 1];
    }
    return GetSize();
}

template <typename KeyType, typename ValueType, typename KeyComparator>
ValueType BPlusTreeInternalPage<KeyType, ValueType, KeyComparator>::
Lookup(const KeyType &key, const KeyComparator &comparator) const
{
    assert(GetSize() > 1);
    if (comparator(key, array[1].first) < 0){ // case 1: key is minimum
        return array[0].second;
    }else if (comparator(key, array[GetSize() - 1].first) >= 0){ // case 2: key is maximum
        return array[GetSize() - 1].second;
    }else { // case 3: key is middle
        // binary search
        int left = 1, right = GetSize() - 1, mid;
        while (left < right && left + 1 != right) {
            mid = left + (right - left) / 2;
            if (comparator(key, array[mid].first) < 0) {
                right = mid;
            } else if (comparator(key, array[mid].first) > 0) {
                left = mid;
            } else {
                return array[mid].second;
            }
        }
        return array[left].second;
    }
}

/*****************************************************************************
 * Helper method to get and set value
 *****************************************************************************/
template <typename KeyType, typename ValueType, typename KeyComparator>
KeyType BPlusTreeInternalPage<KeyType, ValueType, KeyComparator>::
KeyAt(int index) const
{
    // replace with your own code
    assert(0 <= index && index < GetSize());
    return array[index].first;
}

template <typename KeyType, typename ValueType, typename KeyComparator>
void BPlusTreeInternalPage<KeyType, ValueType, KeyComparator>::
SetKeyAt(int index, const KeyType &key)
{
    assert(0 <= index && index < GetSize());
    array[index].first = key;
}

template <typename KeyType, typename ValueType, typename KeyComparator>
int BPlusTreeInternalPage<KeyType, ValueType, KeyComparator>::
ValueIndex(const ValueType &value) const
{
    for (int i = 0; i < GetSize(); ++i){
        if (array[i].second == value){
            return i;
        }
    }
    return GetSize();
}

template <typename KeyType, typename ValueType, typename KeyComparator>
ValueType BPlusTreeInternalPage<KeyType, ValueType, KeyComparator>::
ValueAt(int index) const
{
    assert(0 <= index && index < GetSize());
    return array[index].second;
}

template <typename KeyType, typename ValueType, typename KeyComparator>
void BPlusTreeInternalPage<KeyType, ValueType, KeyComparator>::
SetValueAt(int index, const ValueType &value)
{
    assert(0 <= index && index < GetSize());
    array[index].second = value;
}